package com.tfjybj.itoo.exam.provider.service.impl;

import org.apache.dubbo.config.annotation.Reference;;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.dmsdbj.itoo.tool.base.service.impl.BaseServicePlusImpl;
import com.dmsdbj.itoo.tool.business.ItooResult;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.itextpdf.text.pdf.BaseFont;
import com.tfjybj.itoo.exam.entity.ExaminationEntity;
import com.tfjybj.itoo.exam.entity.ExamineeEntity;
import com.tfjybj.itoo.exam.entity.OptionEntity;
import com.tfjybj.itoo.exam.model.*;
import com.tfjybj.itoo.exam.provider.dao.ExaminationDao;
import com.tfjybj.itoo.exam.provider.dao.ExamineeDao;
import com.tfjybj.itoo.exam.provider.dao.TemplateDao;
import com.tfjybj.itoo.exam.provider.service.*;
import com.tfjybj.itoo.exam.provider.until.ConfigUtil;
import com.tfjybj.itoo.exam.template.ExamineeExportTemp;
import com.tfjybj.itoo.teach.FacadeApi.CourseApi;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.DocumentException;
import org.jplus.hyberbin.excel.utils.ExcelUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.xhtmlrenderer.pdf.ITextRenderer;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static com.tfjybj.itoo.exam.provider.until.DemoConstants.*;

/**
 * ExaminationService接口实现类
 * ${base}表
 *
 * @author 任晓帅
 * @version 2.0.0
 * @since 2.0.0 2018-11-15 19:40:28
 */
@Service("examinationService")
@Slf4j
public class ExaminationServiceImpl extends BaseServicePlusImpl<ExaminationDao, ExaminationEntity> implements ExaminationService {


    @Resource
    private ExaminationDao examinationDao;

    @Resource
    private ExamineeDao examineeDao;

    @Resource
    private JudgeTaskService judgeTaskService;

    @Resource
    private TemplateDao templateDao;

    @Resource
    private CourseScheduleService courseScheduleService;
    private String examcourseid;

    @Resource
    private CourseService courseService;

    @Reference(check = false)
    private CourseApi courseApi;

    @Resource
    private ExamineeService examineeService;

    @Resource
    private ExamRoomServiceImpl examRoomService;

    @Resource
    private OnlineExamServiceImpl onlineExamServiceImpl;

    @Override
    public PageInfo<ExaminationEntity> queryByLikeCourseId(String courseId, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        return new PageInfo<>(examinationDao.queryLikeCourseId(courseId));
    }

    //endregion


    /* **********************************以下为非模板生成的内容********************************* */

    /**
     * 查询所有考试课程
     *
     * @return CourseScheduleEntity 课程实体
     * @author 王雪芬
     * @since 2.0.0     2018-11-19 12:13:16
     */
    @Override
    public List<CourseScheduleModel> queryCourseName() {
        //得到考试课程id
        List<String> courseByidList = examinationDao.querycourseByid();
        List<String> newList = new ArrayList<>();
        for (String courseid : courseByidList) {
            if (!newList.contains(courseid)) {
                newList.add(courseid);
            }
        }

        List<CourseScheduleModel> courseScheduleEntities = new ArrayList<>();
        if (!CollectionUtils.isEmpty(newList)) {
            //课程名字
            courseScheduleEntities = courseScheduleService.querycourseName(newList);
            if (CollectionUtils.isEmpty(courseScheduleEntities)) {

                return null;
            }
        }
        return courseScheduleEntities;
    }

    @Override
    public List<OptionEntity> queryExamOtion(String questionId) {
        return examinationDao.queryExamOtion(questionId);
    }

    /**
     * 根据课程id查询考试名称
     *
     * @param examcourseid String
     * @return ExaminationModel  考试信息
     * @author 王雪芬
     * @since 2.0.0        2018-11-19 09:45:20
     */
    @Override
    public List<ExaminationModel> queryExamName(String examcourseid) {
        if (StringUtils.isEmpty(examcourseid)) {
            return null;
        }
        return examinationDao.queryExamName(examcourseid);
    }

    /**
     * 当前课程下的考试的所有考场信息
     *
     * @param courseId,examNameId
     * @return ExaminationModel 考生信息
     * @author 任晓帅
     * @since 2.0.0 2019年5月14日18:16:23
     */
    @Override
    public List<ExamRoomModel> queryExamRoomInfo(String courseId, String examNameId) {
        return examinationDao.queryExamRoomInfo(courseId, examNameId);

    }

//    /**
//     * 根据考试时间查询考场
//     *
//     * @param
//     * @return ExaminationModel  查询考场
//     * @author 王雪芬
//     * @since 2.0.0       2018-11-19 12:13:35
//     */
//    @Override
//    //    TODO 要删掉的错误代码
//    public List<ExaminationModel>  queryExamSETime(String courseId, String examNameId) {
//        return examinationDao.queryExamSETime(courseId, examNameId);
//    }

    /**
     * 查看所有监考老师
     *
     * @param examId String
     * @return ExamineeReachClassModel  监考老师
     * @author 王雪芬
     * @date 2018年11月19日09:45:47
     */
    @Override
    public List<ExamineeReachClassModel> queryExamTearch(String examId) {
        if (StringUtils.isEmpty(examId)) {
            return null;
        }
        return examinationDao.queryExamTearch(examId);
    }

    /**
     * @param response http信息
     * @param examId   考试id
     * @return Excel表格
     * @author maying
     * @since 2018年11月16日21:50:09
     */
    @Override
    public ItooResult expotrExcel(HttpServletResponse response, String[] examId) {
        Map<Serializable, Serializable> map = new HashMap<>(16);
        //日期转换字符串的格式
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
        map.put(SHEET_NAME, "考生信息");
        //要导出的字段
        map.put(COLUMNS, new String[]{"studentName", "studentCode", "className", "majorName", "collegeName", "loginTime", "beginTime", "endTime", "examinationName", "examRoomName"});
        //导出表格的标题
        map.put(TITLE, "考生信息");
        map.put(NAME, "考生信息表");
        //要导出的数据
        List<ExamineeExportTemp> examinationEntities = new ArrayList<>();
        //   根据前端传过来的  考试id  查询本场考试下面所有的考生
        List<ExamineeModel> examinationModelsList = new ArrayList<>();
        List<ExamineeModel> examinList;
        if (examId.length > 0) {
            for (String exam : examId) {
                examinList = examineeService.selAllExaminee(exam);
                examinationModelsList.addAll(examinList);
            }
        }

        //循环所有考生
        for (ExamineeModel examinationModel : examinationModelsList) {
            ExamineeExportTemp examineeModel = new ExamineeExportTemp();
            BeanUtils.copyProperties(examinationModel, examineeModel);
            examinationEntities.add(examineeModel);
        }
        //为空不进行导出
        if (examinationEntities.size() == 0) {
            return ItooResult.build(ItooResult.FAIL, "没有要导出的数据！");
        }
        map.put(DATA_LIST, (Serializable) examinationEntities);

        //导出
        try {
            ExcelUtil.exportExcel(map, response);
        } catch (Exception e) {
            log.error("导出失败,未知的异常--" + e);
            return ItooResult.build(ItooResult.FAIL, "导出信息失败！");
        }
        return ItooResult.build(ItooResult.SUCCESS, "导出信息成功！");
    }


    /**
     * 根据教师账号查询老师分页所有考试
     *
     * @param operator 教师ID
     * @return 教师所有考试
     * @author 谢妞
     * @since 2018年12月14日19:09:12
     */
    @Override
    public List<ExaminationModel> queryExamByTeacherId(String operator) {
        List<ExaminationModel> examinationModels = examinationDao.queryByOperator(operator);
        return examinationModels;
    }

    /**
     * 查询下拉框
     *
     * @param operator 教师ID
     * @return 下拉框信息
     * @author 谢妞
     * @since 2018年12月26日19:17:53
     */
    @Override
    public List<ExaminationModel> queryDropDown(String operator, Integer type) {
        //查询下拉框中的内容，需要去重之后返回-刘子腾-2018年12月15日21:12:21
        List<ExaminationModel> examinationModels = examinationDao.queryDropDown(operator);
        List<ExaminationModel> examinationModelList = new ArrayList<>();
        List<ExaminationModel> examinationModelArrayList;
        //如果type是0,那么就需要去重
        // 考试类型下拉框
        if (type == 0) {
            examinationModelList = examinationModels.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(ExaminationModel::getClassifyId))), ArrayList::new));
        }
        // 考试名称下拉框
        if (type == 1) {
            examinationModelArrayList = examinationModels.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(ExaminationModel::getExamId))), ArrayList::new));
            examinationModelList.addAll(examinationModelArrayList);
        }
        return examinationModelList;
    }


    /**
     * @param tableArray
     * @return 返回1111或0000
     * @author maying
     * @since 2018年11月17日21:51:36
     */
    @Override
    @Transactional(rollbackOn = Exception.class)
    public ItooResult arrayDelete(List<String> tableArray) {
        // 循环调用删除，配合事务,删除每一场考试的时候删除考试下面所有的考场和考生
        for (String examId : tableArray) {
            //删除考试
//            examId=this.removeById(examId)
            examinationDao.arrayDelete(examId);
            // 删除考场
            examRoomService.delExamroomArray(examId);
            // 删除考生
            examineeService.arrayDelExaminee(examId);
        }
        return ItooResult.build(ItooResult.SUCCESS, "删除成功！");
    }

    /**
     * 根据考试状态查询所有考试信息table表格
     *
     * @param state      状态
     * @param stateTwo   状态
     * @param operatorId 登录人id
     * @return datatable表
     * @author maying
     * @since 2018年11月18日16:00:38
     */
    @Override
    public List<ExaminationModel> selAllExamation(int state, Integer stateTwo, String operatorId) {
        return examinationDao.selAllExamation(state, stateTwo, operatorId);
    }

    /**
     * 首页正在进行开始信息显示
     *
     * @param state      状态
     * @param stateTwo   状态
     * @param operatorId 登录人id
     * @return datatable表
     * @author 任晓帅
     * @since 2019年5月9日16:04:46
     */
    @Override
    public List<ExaminationModel> selExaming(int state, Integer stateTwo, String operatorId) {
        List<ExaminationModel> examingList = examinationDao.selAllExamation(state, stateTwo, operatorId);
        for (ExaminationModel exam : examingList) {
//            String byCourseId = courseService.findByCourseId(exam.getCourseId());
            com.tfjybj.itoo.teach.entity.CourseEntity courseEntity = courseApi.findCourse(exam.getCourseId());
            String strCourse = "";
            if (courseEntity == null) {
                strCourse = "";
            } else {
                strCourse = courseEntity.getName();
            }
            exam.setCourseName(strCourse);
        }
        return examingList;
    }

    /**
     * 通过考试id查询试题存储到redis
     *
     * @param examId   考试id
     * @param operator 操作人
     * @author 王雪芬
     * @since 2.0.0 2018年11月21日16:01:32
     */
    @Override
    public String queryExamOnlineQuestionInfo(String examId, String operator) {
        return examinationDao.queryExamOnlineQuestionInfo(examId, operator);
    }

    /**
     * 通过考试id和操作人查询考试信息
     *
     * @param examId   考试id
     * @param operator 操作人
     * @author 王雪芬
     * @since 2.0.0 2018年11月21日16:01:32
     */
    @Override
    public List<ExamOnlineinforedisModel> examOnlineInfo(String examId, String operator) {
        return examinationDao.examOnlineInfo(examId, operator);
    }

    /**
     * 通过考试id或者操作人从redis中查询试题
     *
     * @param examId 考试id
     * @author 王雪芬
     * @since 2.0.0 2018年11月21日16:01:32
     */
    @Override
    public String queryOnlineCourseById(String examId, String operator) {
        return examinationDao.queryOnlineCourseById(examId, operator);
    }

    /**
     * 组合查询，根据考试id，分类id，考试开始时间，结束时间，班级ID
     *
     * @param examId
     * @param classifyId
     * @param classId
     * @return 符合条件的考试examination
     * @author 谢妞
     * @since 2018年12月14日19:14:22
     */
    @Override
    public List<ExaminationModel> queryCombine(String operator, String examId, String classifyId, String classId) {
        List<ExaminationModel> examinationModels = examinationDao.queryCombine(operator, examId, classifyId, classId);
        return examinationModels;

    }

    @Override
    public List<ExaminationModel> queryExamTimeInfo(String operator) {
        return examinationDao.queryExamTimeInfo(operator);
    }

    @Override
    public boolean insertExaminee(ExamineeModel examineeModel) {
        return examineeService.insertExaminee(examineeModel);
    }

    @Override
    public int selfindExamDuration(String examId) {
        return examinationDao.selfindExamDuration(examId);
    }

    @Override
    public List<ExaminationEntity> editExam(String examName) {
        return examinationDao.selExam(examName);
    }

    @Override
    public ExaminationModel queryExamInfo() {
        return examinationDao.queryExamInfo();
    }

    @Override
    @Transactional(rollbackOn = Exception.class)
    public ItooResult singleDelete(String examId) {
        //删除考试
        examinationDao.arrayDelete(examId);
        // 删除考场
        examRoomService.delExamroomArray(examId);
        // 删除考生
        examineeService.arrayDelExaminee(examId);
        return ItooResult.build(ItooResult.SUCCESS, "删除成功！");
    }

    @Override
    public List<ExaminationModel> getExamResourse() {
        return examinationDao.getExamResourse();
    }

    @Override
    public List<ExaminationEntity> queryExamStudentExaminOnline(List<ExamineeEntity> list) {
        return examinationDao.queryExamStudentExaminOnline(list);
    }

    @Override
    public ExaminationEntity loginExamStudentExaminOnline(String examinationId) {
        return examinationDao.loginExamStudentExaminOnline(examinationId);
    }

    @Override
    public List<ExaminationEntity> selExamName(String examName) {
        return examinationDao.selExamName(examName);
    }

    @Transactional(rollbackOn = Exception.class)
    @Override
    public int updateExamStatus() {
        int beFlag = 0;
        //当前时间大于考试开始时间--在考试范围之内.未更新状态
        List<ExaminationModel> examinationBeModels = examinationDao.queryExamBetweenTime();
        //当前时间大于结束时间---考试结束
        List<ExaminationModel> examinationModels = examinationDao.queryExamEndTime();
        //state =1
        if (!CollectionUtils.isEmpty(examinationBeModels)) {
            beFlag = examinationDao.updateExamBeStatus(examinationBeModels);
            if (beFlag < 0 || beFlag == 0) {
                log.warn("更新考试状态更新失败");
            }
        }
        //state =2 考试结束
        if (!CollectionUtils.isEmpty(examinationModels)) {
            beFlag = examinationDao.updateExamEndStatus(examinationModels);
            if (beFlag < 0 || beFlag == 0) {
                log.warn("更新考试状态更新失败");
            } else {
                //根据模板id查看其他正在进行的考试或者未开始的考试是否在使用
                for (int i = 0; i < examinationModels.size(); i++) {
                    List<ExaminationModel> examinationModelList = examinationDao.selectExamByTemplate(examinationModels.get(i).getTemplateId());
                    if (CollectionUtils.isEmpty(examinationModelList)) {
                        //更新模板状态为未使用
                        boolean flag = templateDao.updataTemplate(examinationModels.get(i).getTemplateId(), "10");
                        if (flag == false) {
                            log.warn("考试结束，更新模板状态失败，考试id{}，模板id{}", examinationModels.get(i).getId(), examinationModels.get(i).getTemplateId());
                        }
                    }
                }
            }
        }
        return beFlag;
    }

    /**
     * 考试信息管理--考试插入
     *
     * @param examinationModel 考试信息model
     * @author 马莹
     * @since 2018-12-13 20:50:54
     */
    @Override
    public String saveExam(ExaminationModel examinationModel) {
        String examId = IdWorker.getIdStr();
        ExaminationEntity examinationEntity = new ExaminationEntity();
        BeanUtils.copyProperties(examinationModel, examinationEntity);
        examinationEntity.setId(examId);
        examinationEntity.setExamClassifyId(examinationModel.getClassifyId());
        examinationEntity.setIsStudentNotes(0);
        examinationEntity.setStudentNotesId("1072112901400514571");
        examinationEntity.setReadTime(30);
        examinationEntity.setTemplateType(2);
        boolean fase = this.save(examinationEntity);
        if (fase) {
            return examId;
        } else {
            return null;
        }
    }

    @Override
    public ExaminationModel queryRecoverExamination(String examinationId) {
        return examinationDao.queryRecoverExamination(examinationId);
    }

    /**
     * 编辑提交功能
     *
     * @return 编辑影响行数
     * @author maying
     * @since 2018-11-23 14:01:30
     */
    @Override
    @Transactional(rollbackOn = Exception.class)
    public boolean updataExamInfomation(ExaminationModel examineeModel) {
        ExaminationEntity examEnter = new ExaminationEntity();
        BeanUtils.copyProperties(examineeModel, examEnter);
//        // 更新考试时间(考试表)
//        this.updateById(examEnter);
//        // 更新考生的考试时长
//        examineeService.updateExamineeInfomation(examEnter.getId(), examEnter.getExamDuration());
//        // 更新考试占用考场时间(理论结束时间)
//        Calendar ca = Calendar.getInstance();
//        ca.setTime(examEnter.getStartTime());
//        ca.add(Calendar.MINUTE, examEnter.getExamDuration());
//        return examRoomService.updateExamRoomInfomation(examEnter.getId(), examEnter.getStartTime(), ca.getTime());
        // 只更新考试时间
        return this.updateById(examEnter);
    }

    /**
     * 根据考试id集合查询考试信息
     *
     * @param examIdList 考试id集合
     * @return List<Examination>
     * @author 马莹
     * @since 2018-12-26 19:56:37
     */
    @Override
    public List<ExaminationModel> selectExam(List<String> examIdList) {
        return examinationDao.selectExam(examIdList);
    }

    @Override
    public ExaminationEntity queryExamination(String examinationId) {
        return examinationDao.queryExamination(examinationId);
    }

    @Override
    public List<String> selectCurrentExam() {
        return examinationDao.selectCurrentExam();
    }


    /**
     * 查询已发布小练
     *
     * @param operator 登录id
     * @return
     * @author 魏恩君
     * @since 2.0.0 2019/1/26
     */
    @Override
    public List<ExaminationModel> queryReleasedPractice(String operator) {
        List<ExaminationModel> list = examinationDao.queryReleasedPractice(operator);
        return list;
    }

    /**
     * 随堂小练教师收卷,更改考试和考生状态,以及汇总学生成绩
     *
     * @param examID 考试id
     * @return
     * @Param state 状态
     * @author 魏恩君
     * @since 2.0.0 2019/1/26
     */

    @Override
    @Transactional(rollbackOn = Exception.class)
    public int updateExamState(String examID, String state, String classId) {
        //更新考试状态
        int flag = examinationDao.updateExamState(examID, state);
        if (flag <= 0) {
            log.error("随堂小练公布答案更新考试状态失败,考试id为{}" + examID);
        }
        //更新考生状态
        int flag1 = +examineeDao.updateExamState(examID);
        if (flag1 <= 0) {
            log.error("随堂小练公布答案更改考生状态失败,考试id为{}" + examID);
        }
        //汇总学生分数
        // 1.修改paper_record表的status为已汇总状态
        // 2.汇总所有学生成绩,插入到student_score表
        // TotalScore totalScore=new TotalScore();
//        totalScore.setExamtionId(examID);
//        totalScore.setJudgeTaskService(judgeTaskService);
//        Thread thread=new Thread(totalScore);
//        thread.run();

        TotalScoreModel totalScore = new TotalScoreModel();
        totalScore.setClassId(classId);
        totalScore.setExaminationId(examID);
        List<TotalScoreModel> examineeModelList = new ArrayList<>();
        examineeModelList.add(totalScore);
        Boolean flag2 = judgeTaskService.totalScoreManyThread(examineeModelList);
        if (flag2 == false) {
            log.error("随堂小练公布答案汇总成绩失败,classId为{},examId为{}" + classId + examID);
        }
        return flag + flag1;
    }


    //    从config.propertites中读取出相应的值
    //            pdf文件生成之后所存放的路径
    private static final String CONTRACT = "../../examPaper/contract";
    //    需要压缩的文件夹所在路径
    private static final String sourceFilePath = "../../examPaper/contract";
    //    zip文件所在路径
    private static final String zipFilePath = "../../examPaper/zip";

    /**
     * @Description: 根据考试Id批量导出学生试卷成PDF文件接口
     * @Param: examinationId:考试id
     * @Param: examinationName: 考试名称
     * @Param: templateId:模板Id
     * @return: 批量导出是否成功的状态
     * @Author: 杨光彩
     * @Date: 5/10/2019
     */
    @Override
    public byte[] exportExamPaperToPdfByExamId(String classId, String examinationId, String examinationName, String templateId) throws FileNotFoundException {
        //        根据考试Id查询本场考试所有的学生信息
        List<ExaminationModel> notExamStudentInfo = examineeService.queryPageExamineeByClassId(classId, examinationId);

        //        计算方法的执行时间，最后可以去掉 TODO
        long startTime = System.currentTimeMillis();
        //        循环学生信息，对每个学生的试卷进行拼接操作
        CountDownLatch doneSignal = new CountDownLatch(notExamStudentInfo.size());
        //创建线程池来存放线程，以防考生数量太多创建太多线程，占用过多资源
        ThreadPoolExecutor executor = new ThreadPoolExecutor(50, 150, 60000, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());
        for (ExaminationModel examineeModel : notExamStudentInfo) {
            //            从model获得考生id
            String examineeId = examineeModel.getExamineId();
            String PDFNAME = examineeModel.getStudentCode();//pdf文件名
            String HTMLNAME = examineeModel.getStudentCode();//html文件名
            //            根据考生id以及模板id查询出该考生的试卷信息
            //使用多线程将每个考生看成一个线程，可以并行进行试卷拼接的操作
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    long startTime1 = System.currentTimeMillis();
                    try {
                        ExamPaperModel examPaperModel = onlineExamServiceImpl.queryPaperAndRecord(examineeId, templateId);
                        //            选择相应的freemarker模板
                        String templateName = "606";
                        Map<String, Object> paramMap = new HashMap<>();
                        paramMap.put("upSerialNumber", UpSerialNumber.values());//大题号
                        paramMap.put("alphabet", Alphabet.values());//        选项ABCD
                        if (examPaperModel != null) {
                            paramMap.put("examPaperModel", examPaperModel);//整个试卷全部信息
                            if (examPaperModel.getPaperQuestionTypeList().size() != 0) {
                                paramMap.put("paperQuestionTypeList", examPaperModel.getPaperQuestionTypeList());//题型及答案信息集合
                            } else {
                                paramMap.put("paperQuestionTypeList", null);//题型及答案信息集合
                            }
                        } else {
                            paramMap.put("examPaperModel", null);//整个试卷全部信息
                            paramMap.put("paperQuestionTypeList", null);//题型及答案信息集合
                        }
                        //            根据考生id查询该学生生的信息
                        ExamineeEntity studentInfo = examineeService.getById(examineeId);
                        paramMap.put("studentInfo", studentInfo);//学生信息

                        contractHandler(templateName, paramMap, PDFNAME, HTMLNAME, examinationName);
                    } catch (Exception e) {
                        log.error("批量打印考试学生试卷失败,该examineeId为{}", examineeId, e);
                    } finally {
                        System.out.println(Thread.currentThread() + " spent time:" + (System.currentTimeMillis() - startTime1));
                        doneSignal.countDown();
                    }
                }
            };
            executor.execute(runnable);
        }
        //        为了让所有的试卷都生成之后再执行压缩以及删除PDF文件，所以需要让主线程等待子线程执行完之后再执行
        try {
            doneSignal.await();
            System.out.println("线程运行时间:" + (System.currentTimeMillis() - startTime) + "ms");
        } catch (InterruptedException e) {
            log.error(e.getMessage(), e);
        }
        //        关闭线程池
        executor.shutdown();
        //        这里定义的是一个考场生成一个文件夹，所以用考场的名字来命名
        String fileName = examinationName;
        boolean flag = fileToZip(sourceFilePath + "/" + examinationName, zipFilePath, fileName);
        if (flag) {
            System.out.println("文件打包成功!");
        } else {
            System.out.println("文件打包失败!");
            return null;
        }

        //打包成功之后把生成的PDF文件全部删除，以防占用服务器资源
        String contractPath = CONTRACT + "/" + examinationName;
        deleteDir(contractPath);
        long endTime = System.currentTimeMillis();
        System.out.println("方法运行时间:" + (endTime - startTime) + "ms");
        //        这里默认定义1：成功  0：失败
        File file = new File(zipFilePath + "/" + fileName + ".zip");
        InputStream in = new FileInputStream(file);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 4];
        int n = 0;
        try {
            while ((n = in.read(buffer)) != -1) {
                out.write(buffer, 0, n);
            }
            out.close();
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }

        return out.toByteArray();
    }



    public byte[] exportExamPaperToPdfByExamId1(String classId, String examinationId, String examinationName, String templateId) throws FileNotFoundException {
        //        根据考试Id查询本场考试所有的学生信息
        List<ExaminationModel> notExamStudentInfo = examineeService.queryPageExamineeByClassId(classId, examinationId);

        //        计算方法的执行时间，最后可以去掉 TODO
        long startTime = System.currentTimeMillis();
        //        循环学生信息，对每个学生的试卷进行拼接操作
        //创建线程池来存放线程，以防考生数量太多创建太多线程，占用过多资源
        //ThreadPoolExecutor executor = new ThreadPoolExecutor(50, 150, 60000, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());
        for (ExaminationModel examineeModel : notExamStudentInfo) {
            //            从model获得考生id
            String examineeId = examineeModel.getExamineId();
            String PDFNAME = examineeModel.getStudentCode();//pdf文件名
            String HTMLNAME = examineeModel.getStudentCode();//html文件名
            //            根据考生id以及模板id查询出该考生的试卷信息
            //使用多线程将每个考生看成一个线程，可以并行进行试卷拼接的操作

            CompletableFuture.runAsync(() -> {
                long startTime1 = System.currentTimeMillis();
                try {
                    ExamPaperModel examPaperModel = onlineExamServiceImpl.queryPaperAndRecord(examineeId, templateId);
                    //            选择相应的freemarker模板
                    String templateName = "606";
                    Map<String, Object> paramMap = new HashMap<>();
                    paramMap.put("upSerialNumber", UpSerialNumber.values());//大题号
                    paramMap.put("alphabet", Alphabet.values());//        选项ABCD
                    if (examPaperModel != null) {
                        paramMap.put("examPaperModel", examPaperModel);//整个试卷全部信息
                        if (examPaperModel.getPaperQuestionTypeList().size() != 0) {
                            paramMap.put("paperQuestionTypeList", examPaperModel.getPaperQuestionTypeList());//题型及答案信息集合
                        } else {
                            paramMap.put("paperQuestionTypeList", null);//题型及答案信息集合
                        }
                    } else {
                        paramMap.put("examPaperModel", null);//整个试卷全部信息
                        paramMap.put("paperQuestionTypeList", null);//题型及答案信息集合
                    }
                    //            根据考生id查询该学生生的信息
                    ExamineeEntity studentInfo = examineeService.getById(examineeId);
                    paramMap.put("studentInfo", studentInfo);//学生信息

                    contractHandler(templateName, paramMap, PDFNAME, HTMLNAME, examinationName);
                } catch (Exception e) {
                    log.error("批量打印考试学生试卷失败,该examineeId为{}", examineeId, e);
                } finally {
                    System.out.println(Thread.currentThread() + " spent time:" + (System.currentTimeMillis() - startTime1));
                }
            });

            //executor.execute(runnable);
        }
        //        为了让所有的试卷都生成之后再执行压缩以及删除PDF文件，所以需要让主线程等待子线程执行完之后再执行

        System.out.println("线程运行时间:" + (System.currentTimeMillis() - startTime) + "ms");

        //        关闭线程池
        //executor.shutdown();
        //        这里定义的是一个考场生成一个文件夹，所以用考场的名字来命名
        String fileName = examinationName;
        boolean flag = fileToZip(sourceFilePath + "/" + examinationName, zipFilePath, fileName);
        if (flag) {
            System.out.println("文件打包成功!");
        } else {
            System.out.println("文件打包失败!");
            return null;
        }

        //打包成功之后把生成的PDF文件全部删除，以防占用服务器资源
        String contractPath = CONTRACT + "/" + examinationName;
        deleteDir(contractPath);
        long endTime = System.currentTimeMillis();
        System.out.println("方法运行时间:" + (endTime - startTime) + "ms");
        //        这里默认定义1：成功  0：失败
        File file = new File(zipFilePath + "/" + fileName + ".zip");
        InputStream in = new FileInputStream(file);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 4];
        int n = 0;
        try {
            while ((n = in.read(buffer)) != -1) {
                out.write(buffer, 0, n);
            }
            out.close();
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }

        return out.toByteArray();
    }

    public void contractHandler(String templateName,
                                Map<String, Object> paramMap, String pdfName, String htmlName, String examinationName) throws Exception {
        // 获取本地模板存储路径、合同文件存储路径
        String fileUrl = this.getClass().getClassLoader().getResource("templates").getPath();
        String templatePath = fileUrl;
        String contractPath = CONTRACT;
        // 组装html和pdf合同的全路径URL
        String localHtmlUrl = contractPath + htmlName + ".html";
        String localPdfPath = contractPath + "/" + examinationName;
        // 判断本地路径是否存在如果不存在则创建
        File localFile = new File(localPdfPath);
        if (!localFile.exists()) {
            localFile.mkdirs();
        }
        String localPdfUrl = localFile + "/" + pdfName + ".pdf";
        templateName = templateName + ".ftl";
        htmHandler(templatePath, templateName, localHtmlUrl, paramMap);// 生成html合同
        pdfHandler(localHtmlUrl, localPdfUrl);// 根据html合同生成pdf合同
        deleteFile(localHtmlUrl);// 删除html格式合同
        System.out.println("PDF生成成功");
    }

    /**
     * @Description: 由ftl模板生成HTML文件
     * @Param:templatePath 模板路径
     * @param:templateName 模板名称
     * @param:htmUrl html的URL
     * @param:paramMap 存放参数的map
     * @return: 4:13 PM
     * @Author: 杨光彩
     * @Date: 5/16/2019
     */
    private static void htmHandler(String templatePath, String templateName,
                                   String htmUrl, Map<String, Object> paramMap) throws Exception {
        Configuration cfg = new Configuration();
        cfg.setDefaultEncoding("UTF-8");
        cfg.setDirectoryForTemplateLoading(new File(templatePath));

        Template template = cfg.getTemplate(templateName);

        File outHtmFile = new File(htmUrl);

        Writer out = new BufferedWriter(new OutputStreamWriter(
                new FileOutputStream(outHtmFile), "utf-8"));
        template.process(paramMap, out);

        out.close();
    }

    /**
     * @Description: 由html生成PDF
     * @Param:htmUrl html的URL
     * @param: pdfUrl pdf的URL
     * @return: 4:15 PM
     * @Author: 杨光彩
     * @Date: 5/16/2019
     */
    private void pdfHandler(String htmUrl, String pdfUrl)
            throws DocumentException, IOException {
        File htmFile = new File(htmUrl);
        File pdfFile = new File(pdfUrl);

        String url = htmFile.toURI().toURL().toString();

        OutputStream os = new FileOutputStream(pdfFile);

        ITextRenderer renderer = new ITextRenderer();
        renderer.setDocument(url);

        org.xhtmlrenderer.pdf.ITextFontResolver fontResolver = renderer
                .getFontResolver();
        // 解决中文支持问题
        String fileUrl = this.getClass().getClassLoader().getResource("templates/simsun.ttc").getPath();
        try {
            fontResolver.addFont(fileUrl,
                    BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        } catch (com.itextpdf.text.DocumentException e) {
            log.error("由html生成pdf失败", e);
        }

        try {
            fontResolver.addFont(fileUrl, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        } catch (com.itextpdf.text.DocumentException e) {
            log.error("由html生成pdf失败", e);
        }

        renderer.layout();
        try {
            renderer.createPDF(os);
            renderer.finishPDF();
        } catch (com.itextpdf.text.DocumentException e) {
            log.error("由html生成pdf失败", e);
        }
        os.close();
    }

    /**
     * @Description: 删除html文件
     * @Param:fileUrl html的URL
     * @return: 4:17 PM
     * @Author: 杨光彩
     * @Date: 5/16/2019
     */
    private static void deleteFile(String fileUrl) {
        File file = new File(fileUrl);
        file.delete();
    }

    /**
     * @Description: 迭代删除文件夹或者文件
     * @Param:dirPath 文件夹或者文件的路径
     * @return: 4:17 PM
     * @Author: 杨光彩
     * @Date: 5/16/2019
     */
    public static void deleteDir(String dirPath) {
        File file = new File(dirPath);
        if (file.isFile()) {
            log.info("1" + file.getAbsolutePath() + file.delete());
        } else {
            File[] files = file.listFiles();
            if (files == null) {
                log.info("2" + file.getAbsolutePath() + file.delete());
            } else {
                for (int i = 0; i < files.length; i++) {
//                    递归删除文件
                    deleteDir(files[i].getAbsolutePath());
                }
                log.info("3" + file.getAbsolutePath() + file.delete());
            }
        }
    }

    /**
     * 将存放在sourceFilePath目录下的源文件，打包成fileName名称的zip文件，并存放到zipFilePath路径下
     *
     * @param sourceFilePath :待压缩的文件路径
     * @param zipFilePath    :压缩后存放路径
     * @param fileName       :压缩后文件的名称
     * @return
     */
    public static boolean fileToZip(String sourceFilePath, String zipFilePath, String fileName) {
        boolean flag = false;
        File sourceFile = new File(sourceFilePath);
        File zipSourceFile = new File(zipFilePath);
//        如果zip文件不存在，则生成
        if (!zipSourceFile.exists()) {
            zipSourceFile.mkdirs();
        }
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        FileOutputStream fos = null;
        ZipOutputStream zos = null;

        if (sourceFile.exists() == false) {
            System.out.println("待压缩的文件目录：" + sourceFilePath + "不存在.");
        } else {
            try {
                File zipFile = new File(zipFilePath + "/" + fileName + ".zip");
                File[] sourceFiles = sourceFile.listFiles();
                if (null == sourceFiles || sourceFiles.length < 1) {
                    System.out.println("待压缩的文件目录：" + sourceFilePath + "里面不存在文件，无需压缩.");
                } else {
                    fos = new FileOutputStream(zipFile);
                    zos = new ZipOutputStream(new BufferedOutputStream(fos));
                    byte[] bufs = new byte[1024 * 10];
                    for (int i = 0; i < sourceFiles.length; i++) {
                        //创建ZIP实体，并添加进压缩包
                        ZipEntry zipEntry = new ZipEntry(sourceFiles[i].getName());
                        zos.putNextEntry(zipEntry);
                        //读取待压缩的文件并写进压缩包里
                        fis = new FileInputStream(sourceFiles[i]);
                        bis = new BufferedInputStream(fis, 1024 * 10);
                        int read = 0;
                        while ((read = bis.read(bufs, 0, 1024 * 10)) != -1) {
                            zos.write(bufs, 0, read);
                        }
//                        在循环里面一定要关闭
                        if (null != bis) bis.close();
                        if (null != fis) fis.close();
                    }
                    flag = true;
                }
            } catch (FileNotFoundException e) {
                log.error("pdf压缩成zip失败", e);
                throw new RuntimeException(e);
            } catch (IOException e) {
                log.error("pdf压缩成zip失败", e);
                throw new RuntimeException(e);
            } finally {
                //关闭流
                try {
                    if (null != bis) bis.close();
                    if (null != fis) fis.close();

                    if (null != zos) zos.close();
                    if (null != fos) fos.close();
                } catch (IOException e) {
                    log.error("pdf压缩成zip失败", e);
                    throw new RuntimeException(e);
                }
            }
        }
        return flag;
    }

    // 选项字母
    public enum Alphabet {
        A, B, C, D, E, F, G
    }

    // 大题号
    public enum UpSerialNumber {
        一, 二, 三, 四, 五, 六, 七, 八, 九, 十, 十一, 十二, 十三, 十四, 十五, 十六, 十八, 十九, 二十, 二一, 二二, 二三, 二四, 二五
    }

}